Skip to content

chore: relax requires-python floor to >= 3.11#366

Merged
max-parke-scale merged 3 commits into
nextfrom
mparke/relax-python-floor-to-3.11
May 26, 2026
Merged

chore: relax requires-python floor to >= 3.11#366
max-parke-scale merged 3 commits into
nextfrom
mparke/relax-python-floor-to-3.11

Conversation

@max-parke-scale
Copy link
Copy Markdown
Contributor

@max-parke-scale max-parke-scale commented May 26, 2026

Summary

  • Lower requires-python from >= 3.12,<4>= 3.11,<4.
  • Add Python :: 3.11 to the classifiers.

Why

scaleapi/scaleapi's packages/egp-api-backend (and 7 other monorepo packages) are pinned to >=3.11,<3.12, blocking adoption of this SDK there. Specifically, SGP-5968 (Insights Generator backend) is swapping from scale-gp-agents to agentex-sdk and Poetry version resolution fails on the current >= 3.12 floor.

Safety

The SDK source parses cleanly at Python 3.11 grammar — no PEP 695 type parameters, no type statement, no other 3.12-only syntax.

Verified with:

import ast, os
for root, _, files in os.walk('agentex'):
    for f in files:
        if not f.endswith('.py'): continue
        with open(os.path.join(root, f)) as fh:
            ast.parse(fh.read(), feature_version=(3, 11))
# 0 syntax errors across all 200+ .py files

The Stainless-generated 3.12 floor appears to be a default, not a real syntactic constraint.

Coordination

Declan pre-approved this relax in conversation with @max-parke-scale.

Test plan

  • CI green
  • After merge, cut a patch release (v0.11.4) so consumers can update their pin

🤖 Generated with Claude Code

Greptile Summary

This PR relaxes the requires-python floor from >=3.12,<4 to >=3.11,<4 and adds the Programming Language :: Python :: 3.11 PyPI classifier, unblocking adoption in monorepo packages pinned to >=3.11,<3.12.

  • requires-python + classifier: Single-line change in pyproject.toml; the PR's AST-parse verification confirms no 3.12-only syntax is used across the 200+ source files.
  • Pyright config: [tool.pyright] pythonVersion remains at 3.12 (flagged in a prior review pass), so type-level 3.11/3.12 differences won't be caught by static analysis until that is updated.
  • CI coverage: The test job has no Python version matrix, so runtime compatibility on 3.11 is unvalidated beyond the AST check.

Confidence Score: 5/5

Safe to merge — the change is a one-line metadata relaxation with no modifications to SDK logic.

The diff touches only the requires-python specifier and a PyPI classifier. The author verified zero syntax errors against the 3.11 grammar across all source files, and the Stainless-generated 3.12 floor was a tooling default rather than a real language constraint. No runtime logic, dependencies, or build steps are affected.

No files require special attention; the only changed file is pyproject.toml with a two-line metadata edit.

Important Files Changed

Filename Overview
pyproject.toml Lowers requires-python floor from >=3.12 to >=3.11 and adds the corresponding PyPI classifier; syntactically clean, but [tool.pyright] pythonVersion remains at 3.12 and CI has no 3.11 matrix job.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Consumer project\n(pinned to Python >=3.11,<3.12)"] -->|"Before: Poetry resolution fails"| B["agentex-sdk\nrequires-python >= 3.12"]
    A -->|"After: resolves ✓"| C["agentex-sdk\nrequires-python >= 3.11"]
    C --> D["Python 3.11 runtime"]
    C --> E["Python 3.12 runtime"]
    C --> F["Python 3.13 runtime"]
    B -.->|"blocked"| D
Loading

Comments Outside Diff (1)

  1. pyproject.toml, line 193-196 (link)

    P2 The requires-python floor was lowered to 3.11, but pythonVersion in the pyright config still targets 3.12. This means pyright validates stdlib types and overloads against 3.12 semantics, so any type-level incompatibilities between 3.11 and 3.12 (e.g., tomllib being stdlib in 3.11 vs not, ExceptionGroup stdlib availability differences in stub resolution, or any sys.version_info-gated overloads) won't be caught by CI when running on a 3.11 host.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: pyproject.toml
    Line: 193-196
    
    Comment:
    The `requires-python` floor was lowered to 3.11, but `pythonVersion` in the pyright config still targets 3.12. This means pyright validates stdlib types and overloads against 3.12 semantics, so any type-level incompatibilities between 3.11 and 3.12 (e.g., `tomllib` being stdlib in 3.11 vs not, `ExceptionGroup` stdlib availability differences in stub resolution, or any `sys.version_info`-gated overloads) won't be caught by CI when running on a 3.11 host.
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Cursor Fix in Claude Code Fix in Codex

Fix All in Cursor Fix All in Claude Code Fix All in Codex

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
pyproject.toml:56
**No CI matrix job validates Python 3.11 at runtime**

The CI `test` job uses Rye's default Python (pulled from `pyproject.toml`'s `[tool.rye] python` or the runner default, not a version matrix), so no job actually installs and runs the test suite under Python 3.11. The `ast.parse` check in the PR description only validates syntax — it won't catch usage of 3.12-only stdlib additions (e.g., `itertools.batched`, `pathlib.Path.is_junction`, `os.process_cpu_affinity`) or behaviour differences. Adding a `python-version: ["3.11", "3.12"]` matrix entry to the `test` job in `.github/workflows/ci.yml` would give true confidence for the lowered floor.

Reviews (4): Last reviewed commit: "Revert "chore: align pyright pythonVersi..." | Re-trigger Greptile

Lets the SDK install on Python 3.11 consumers (most notably
scaleapi/scaleapi's egp-api-backend, which is pinned to 3.11 alongside
7 other monorepo packages). Verified the SDK source parses cleanly at
ast.parse(feature_version=(3, 11)) over every .py file — no PEP 695
type params, no type statement, no other 3.12-only syntax.

Declan approved the relax in conversation with Max Parke.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@max-parke-scale max-parke-scale changed the base branch from main to next May 26, 2026 16:49
@max-parke-scale max-parke-scale marked this pull request as draft May 26, 2026 16:54
@max-parke-scale
Copy link
Copy Markdown
Contributor Author

Closing as obsoleted — superseded by Stainless config update.

The Stainless project config for agentex now sets targets.python.options.minimum_python_version: "3.11". The next Stainless regen will emit requires-python = ">= 3.11,<4" (plus the Python :: 3.11 classifier) into pyproject.toml natively, no manual patch needed.

This PR's one-line change would only persist as a manual override against the generator, so closing in favor of the config-side fix.

Reopen-able if the regen doesn't produce the expected change.

🤖 — posted via Claude Code

@max-parke-scale max-parke-scale marked this pull request as ready for review May 26, 2026 17:39
@max-parke-scale
Copy link
Copy Markdown
Contributor Author

Reopened — Stainless's python_version_range config option turned out not to fully solve the issue:

  • Schema rejected the compound range ">= 3.11,<4" (no commas allowed in this field).
  • The unbounded ">= 3.11" was accepted, but Stainless's lockfile-generation pipeline runs uv compile on Python 3.9.18 and refuses to lock a project requiring >= 3.11. Build fails at requirements.lock step.

So the manual pyproject.toml patch in this PR is the unblock path. Stainless's 3-way merge should preserve it across regens per CONTRIBUTING.md.

Separate Stainless-side concern (won't block this PR): their lockfile pipeline should select a Python interpreter that satisfies requires-python, not fall back to the container's 3.9.18. Worth a support ticket — but not for this PR to fix.

Cc @declan-scale if you can hit the merge button when CI's green, this unblocks SGP-5968 → unblocks #143807 → unblocks the v6 IG flag-on.

🤖 — posted via Claude Code

@max-parke-scale
Copy link
Copy Markdown
Contributor Author

Follow-up data point — Stainless's python_version_range config field appears non-functional, at least for this project.

After the ">= 3.11" attempt failed at lockfile generation, we tried python_version_range: ">= 3.9". Stainless's build completed successfully this time (no lockfile error since 3.9.18 satisfies >= 3.9), but the regenerated pyproject.toml on next still emitted requires-python = ">= 3.12,<4". The config option seems to be silently ignored at the codegen step — accepted by the schema, passes the build, but doesn't propagate into the generated SDK metadata.

Two possibilities:

  • The field needs to be paired with an explicit edition: on the python target (agentex's config has no edition; default may hardcode 3.12).
  • The field is intended for something else (maybe a downstream dev/test-environment constraint) and doesn't actually control the SDK's requires-python line.

Either way: the config-side fix is not an off-the-shelf 5-minute change. This PR (manual pyproject.toml patch) is the unblock path; the Stainless field issue needs a support ticket to track separately.

Separately re. pythonVersion = "3.12" under [tool.pyright] at pyproject.toml:~196: that's the pyright type-checker's target version (used for type analysis), not the runtime floor — independent of requires-python and the SDK's actual Python compatibility. Doesn't affect installability or anything user-facing; safe to leave at 3.12 (means the SDK is type-checked against 3.12 features even though it now installs on 3.11). Could be flipped to 3.11 later for consistency, but it's not load-bearing for this PR.

🤖 — posted via Claude Code

Address Greptile P2: pyright was still targeting 3.12 while the runtime
floor was lowered to 3.11. Aligning so CI type-checks against 3.11
semantics — catches 3.11-vs-3.12 stdlib/overload drift that would
otherwise slip through.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@max-parke-scale
Copy link
Copy Markdown
Contributor Author

Addressed Greptile's P2 on pyproject.toml:193-196 in commit 66a7369pythonVersion in [tool.pyright] flipped from "3.12""3.11" to match the new requires-python floor. Pyright will now type-check against 3.11 semantics, so any 3.11-vs-3.12 stdlib/overload drift gets caught at CI rather than slipping through.

🤖 — posted via Claude Code

@max-parke-scale
Copy link
Copy Markdown
Contributor Author

Reverting commit 66a7369 — the pyright pythonVersion realignment was wrong; pyright at 3.12 is correct as-is.

When I flipped pythonVersion from "3.12""3.11", CI surfaced 116 lint errors: "override" is unknown import symbol across agentex/lib/, plus reportImplicitOverride everywhere it's used.

The root cause: this SDK has two distinct kinds of code with different Python floors:

  • Client surface (agentex/, agentex/types/, agentex/resources/) — Stainless-generated, uses from typing_extensions import override → installs and imports cleanly on Python 3.8+. This is what consumers like egp-api-backend import via from agentex import AsyncAgentex.
  • Framework code (agentex/lib/) — hand-written, uses from typing import override → requires Python 3.12+. Imported only by agent-side code (e.g., agents running inside the Agentex framework, which run on 3.12+ by convention), never by client consumers.

Crucially agentex/__init__.py does NOT import agentex.lib, so installing + using the client surface on Python 3.11 works fine. The requires-python = ">= 3.11" claim is accurate for the client-side use case (which is the primary install path for this PR).

So pyright at 3.12 is the right setting because it covers the highest-floor portion of the codebase (agentex/lib/). Lowering it would surface false positives in code that legitimately requires 3.12.

Greptile's concern would be valid if the entire SDK was claiming 3.11 support, but the actual semantic claim is: "client surface works on 3.11+; framework code requires 3.12+." A cleaner long-term fix is to migrate the agentex/lib/ from typing import override calls to from typing_extensions import override (13 files, mechanical change) — but that's a separate refactor, out of scope for this PR's requires-python relax.

🤖 — posted via Claude Code

@max-parke-scale max-parke-scale merged commit a064f92 into next May 26, 2026
37 checks passed
@max-parke-scale max-parke-scale deleted the mparke/relax-python-floor-to-3.11 branch May 26, 2026 19:08
@stainless-app stainless-app Bot mentioned this pull request May 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants